﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ParserCore;

namespace TestParser
{
    [TestClass]
    public partial class ParserUnitTestIvan2029
    {
        [TestMethod]
        public void WordListCommandTest()
        {
            var expression = "com arg1 arg2 arg3";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(
                 "com(arg1,arg2,arg3)"
                 , statement.ToString());
        }

        [TestMethod]
        public void CallTest()
        {
            var expression = "com ( 1 + 2 , 3 + 4 )";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(
                "com(+(1,2),+(3,4))"
                , statement.ToString());
        }

        [TestMethod]
        public void CallColonTest()
        {
            var expression = "0:pi/10:pi";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(
                ":(0,/(pi,10),pi)",
                statement.ToString());
        }

        [TestMethod]
        public void AssignmentTest()
        {
            var expression = "com =  1 + 2";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(
                new AssignmentStatement(
                    new Expression[] { new Identifier("com") },
                    new Call(
                        new Identifier("+"),
                        new Constant("1", Constant.CType.RealNumber),
                        new Constant("2", Constant.CType.RealNumber)
                        )),
                statement);
        }

        [TestMethod]
        public void BlockStatementTest()
        {
            var expression = "1, , x";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(
                new BlockStatement(
                    new Statement[] { new SingleExpressionStatement(new Constant("1", Constant.CType.RealNumber)),
                                      new SingleExpressionStatement(new Identifier("x")) }),
                    statement);
        }

        [TestMethod]
        public void NDimArrayBuilderEmptyTest()
        {
            var expression = "[]";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);

            Assert.AreEqual(
                "[]",
                statement.ToString()
                );
        }

        [TestMethod]
        public void NDimArrayBuilderTest()
        {
            var expression = "[ 1 2 ; 3 4 ]";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);

            Assert.AreEqual(
                 "[1,2;3,4]",
                 statement.ToString()
                );
        }

        [TestMethod]
        public void EmptyStatementBlockTest()
        {
            Assert.AreEqual(
                new BlockStatement(new Statement[] { }),
                new BlockStatement(new Statement[] { })
                );
        }

        [TestMethod]
        public void ScriptTest()
        {
            var expression = "x\n y\n z";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression, true);
            Assert.AreEqual(
                new BlockStatement(
                    new Statement[] {
                        new SingleExpressionStatement(new Identifier("x")),
                        new SingleExpressionStatement(new Identifier("y")),
                        new SingleExpressionStatement(new Identifier("z")),
                    }),
                    statement
                );
        }

        [TestMethod]
        public void IfThenElseTest1()
        {
            var expression = "if x\n y\n end";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(statement,
                new IfThenElseStatement(
                    new Identifier("x"),
                    new SingleExpressionStatement(new Identifier("y")),
                    new BlockStatement(new Statement[] { })
                    ));
        }

        [TestMethod]
        public void FcnHandleTest()
        {
            var expression = "@ime";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(
                "_at_(ime)",
                statement.ToString());
        }

        [TestMethod]
        public void IfThenElseTest2()
        {
            var expression = "if x\ny\nelseif z\n t\nelse w\nend";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(statement,
                new IfThenElseStatement(
                    new Identifier("x"),
                    new SingleExpressionStatement(new Identifier("y")),
                    new IfThenElseStatement(
                        new Identifier("z"),
                        new SingleExpressionStatement(new Identifier("t")),
                        new SingleExpressionStatement(new Identifier("w"))
                        )
                    ));
        }

        [TestMethod]
        public void StructFunctionCallTest()
        {
            var expression = "containers.map ( arg )";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(
                ".(containers,map)(arg)",
                statement.ToString()
                );
        }

        [TestMethod]
        public void CellBuilderTest()
        { // ovaj test trenutno pada zato sto CellBuilder nema implementiran Equals metod
            var expression = "{ x, 1 ; 2, y }";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(
                new SingleExpressionStatement(
                new CellBuilder(
                    new Expression[][] {
                        new Expression[] { new Identifier("x"),
                                           new Constant("1", Constant.CType.RealNumber) 
                        },
                        new Expression[] { new Constant("2", Constant.CType.RealNumber),
                                           new Identifier("y") 
                        }
                    }
                    )),
                statement);
        }

        [TestMethod]
        public void DeclarationTest()
        {
            var globexpr = "global x y";
            var persexpr = "persistent x y";
            var parser = new ParsingEngine();
            var globstat = parser.Parse(globexpr);
            var persstat = parser.Parse(persexpr);

            Assert.AreEqual(
                new GlobalDeclarationStatement(
                    new Identifier("x"),
                    new Identifier("y")),
                globstat);
            Assert.AreEqual(
                new PersistentDeclarationStatement(
                    new Identifier("x"),
                    new Identifier("y")
                    ),
                persstat);
        }

        [TestMethod]
        public void AnonFcnTest()
        {
            var expression1 = "@(x, y) x + y";
            var expression2 = "@(x) @(y) x + y";
            var parser = new ParsingEngine();
            var statement1 = parser.Parse(expression1);
            var statement2 = parser.Parse(expression2);

            Assert.AreEqual(
                new SingleExpressionStatement(
                    new AnonymousFunctionBuilder(
                        new Identifier[] {
                          new Identifier("x"),
                          new Identifier("y")},
                        new Call(new Identifier("+"),
                            new Identifier("x"),
                            new Identifier("y"))
                        )),
                statement1);

            Assert.AreEqual(
                new SingleExpressionStatement(
                    new AnonymousFunctionBuilder(
                        new Identifier[] { new Identifier("x") },
                        new AnonymousFunctionBuilder(
                            new Identifier[] { new Identifier("y") },
                            new Call(new Identifier("+"),
                                new Identifier("x"),
                                new Identifier("y"))))
                    ),
                statement2);
        }

        [TestMethod]
        public void FunctionDefTest()
        {
            var expression = "function x = f(y) \n x = y \n end";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(
                new FunctionDefinitionStatement(
                    new Identifier("f"),
                    new Identifier[] { new Identifier("x") },
                    new Identifier[] { new Identifier("y") },
                    new AssignmentStatement(
                        new Expression[] { new Identifier("x") },
                        new Identifier("y")
                    )),
                statement);
        }

        [TestMethod]
        public void InvalidStringTest()
        {
            var expression1 = "'invalid string";
            var expression2 = "'valid string'";
            var parser = new ParsingEngine();
            var statement1 = parser.Parse(expression1);
            var statement2 = parser.Parse(expression2);

            Assert.AreEqual(null, statement1);
            Assert.AreEqual("valid string", statement2.ToString());
        }

        [TestMethod]
        public void XCellCellTest()
        {
            var expression = "X{1,1}{1,1}";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(
                "X{1,1}{1,1}",
                statement.ToString());
        }

        [TestMethod]
        public void FunctionMatfTest1()
        {
            var expression = @"function p=pozitivnenule()
                               nule;
                               Y=[];
                               for i=1:length(X)
                                  if X(i)>=0
                                    Y=[Y X(i)];
                                  end
                               end
                               p=poly(Y); ";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);
            Assert.AreEqual(
                "function[p]=pozitivnenule(){nule;[Y]=[];for(i=:(1,length(X))){if(>=(X(i),0)){[Y]=[Y,X(i)]}else{}};[p]=poly(Y)}",
                statement.ToString());
        }

        [TestMethod]
        public void NestedIfThenElse()
        {
            var expression =
                @"if x1
                    if y1
                      z1
                    elseif y2
                      z2
                    else
                      z3
                    end
                  elseif x2
                    if y3
                      z4
                    end
                  elseif x3
                    y4
                  else
                    y5
                  end
                ";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);

            Assert.AreEqual(
                "if(x1){if(y1){z1}else{if(y2){z2}else{z3}}}else{if(x2){if(y3){z4}else{}}else{if(x3){y4}else{y5}}}",
                statement.ToString());
        }

        [TestMethod]
        public void NestedSwitchCase()
        {
            var expression =
                @"switch x1
                    case 1
                        switch y1
                            case 1
                                z1
                            otherwise
                                z2
                        end
                    case 2
                        y2
                    otherwise
                        y3
                    end
                ";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);

            Assert.AreEqual(
                "switch(x1)case(1){switch(y1)case(1){z1}otherwise{z2}}case(2){y2}otherwise{y3}",
                statement.ToString());
        }

        [TestMethod]
        public void ColonEndTest()
        {
            var expression = "A(3:end)";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);

            Assert.AreEqual("A(:(3,_end_))", statement.ToString());
        }

        [TestMethod]
        public void NestedFunctionsTest()
        {
            var expression =
                @"function D = stalker(d,e)
                        D = degtyarev(e, d);
                  
                  function [A, B] = sidorovich(a,b)
                        A = 'marked one';
                        B = a + b;
                  end
                
                  function D = degtyarev(c, d)
                    D = pripyat(c) + janov(d);

                    function A = pripyat(a)
                        A = a + 1;
                    end
            
                    function B = janov(b)
                        B = b * 2;
                    end
                  end
                ";

            var parser = new ParsingEngine();
            var statement = parser.Parse(expression);

            Assert.AreEqual(
                "function[D]=stalker(d,e){[D]=degtyarev(e,d);"
              + "function[A,B]=sidorovich(a,b){[A]=marked one;"
              + "[B]=+(a,b)};function[D]=degtyarev(c,d)"
              + "{[D]=+(pripyat(c),janov(d));function[A]" 
              + "=pripyat(a){[A]=+(a,1)};function[B]=janov(b){[B]=*(b,2)}}}",
                statement.ToString());
        }


        [TestMethod]
        public void MatlabQRTest()
        {
            var expression =
                @"function [Q,R,E] = qr(A,econ)
                %Embedded MATLAB Library Function

                %   Copyright 2002-2009 The MathWorks, Inc.
                %#eml

                eml_assert(nargin > 0, 'Not enough input arguments.');
                eml_assert(nargin == 1 || (isa(econ,'numeric') && isscalar(econ)), ...
                    'Use qr(X,0) for economy size decomposition.');
                eml_assert(isa(A,'float'), ...
                    ['Function ''qr'' is not defined for values of class ''' class(A) '''.']);
                eml_lib_assert(ndims(A) == 2, 'EmbeddedMATLAB:qr:inputMustBe2D', ...
                    'Argument A must be a 2-D matrix.');
                eml_lib_assert(nargin == 1 || econ == 0, ...
                    'MATLAB:qr:unknownOptionForEconomySizeDecomposition', ...
                    'Use qr(X,0) for economy size decomposition.');
                if nargout == 3 
                    if nargin == 1
                        [Q,R,E] = qr_full(A);
                    else
                        [Q,R,E] = qr_econ(A);
                    end
                elseif nargout == 2
                    if nargin == 1
                        [Q,R] = qr_full(A);
                    else    
                        [Q,R] = qr_econ(A);
                    end
                else
                    Q = eml_zlaqp2(A);
                end

                %--------------------------------------------------------------------------

                function [Q,R,E] = qr_full(A)
                % Full QR with or without pivoting.
                eml_must_inline;
                m = size(A,1);
                n = size(A,2);
                if ~eml_is_const(n)
                    eml.varsize('Q','tau');
                end
                % Q is m x m, R is m x n
                Q = eml.nullcopy(eml_expand(eml_scalar_eg(A),[m,m]));
                R = eml.nullcopy(A);
                if nargout == 3
                    E = zeros(n,class(A));
                end
                if m > n
                    % Q = [A,zeros(m,m-n)];
                    for j = 1:n
                        for i = 1:m
                                Q(i,j) = A(i,j);
                        end
                    end
                    for j = n+1:m
                        for i = 1:m
                            Q(i,j) = 0;
                        end
                    end
                    if nargout == 3 
                        [Q,tau,jpvt] = eml_zlaqp2(Q);
                    else
                        [Q,tau] = eml_zlaqp2(Q);
                    end
                    for j = 1:n
                        for i = 1:j
                            R(i,j) = Q(i,j);
                        end
                        for i = j+1:m
                            R(i,j) = 0;
                        end
                    end
                    Q = eml_zung2r(Q,tau);
                else
                    if nargout == 3
                        [A,tau,jpvt] = eml_zlaqp2(A);
                    else
                        [A,tau] = eml_zlaqp2(A);
                    end
                    for j = 1:m
                        for i = 1:j
                            R(i,j) = A(i,j);
                        end
                        for i = j+1:m
                            R(i,j) = 0;
                        end
                    end
                    for j = m+1:n
                        for i = 1:m
                            R(i,j) = A(i,j);
                        end
                    end
                    Q = eml_zung2r(A,tau);
                end
                if nargout == 3
                    % Create a permutation matrix from jpvt.
                    for k = 1:n
                        E(jpvt(k),k) = 1;
                    end
                end

                %--------------------------------------------------------------------------

                function [Q,R,E] = qr_econ(A)
                % Economy size QR with pivoting.
                eml_must_inline;
                if nargout == 3
                    [A,tau,jpvt] = eml_zlaqp2(A);
                    E = double(jpvt);
                else
                    [A,tau] = eml_zlaqp2(A);
                end
                Q = eml_zung2r(A,tau);
                m = size(A,1);
                n = size(A,2);
                if m > n
                    % Q is m x n, R is n x n
                    R = eml.nullcopy(eml_expand(eml_scalar_eg(A),[n,n]));
                    for j = 1:n
                        for i = 1:j
                            R(i,j) = A(i,j);
                        end
                        for i = j+1:n
                            R(i,j) = 0;
                        end
                    end
                else
                    % Q is m x m, R is m x n
                    R = eml.nullcopy(A);
                    for j = 1:m
                        for i = 1:j
                            R(i,j) = A(i,j);
                        end
                        for i = j+1:m
                            R(i,j) = 0;
                        end
                    end
                    for j = m+1:n
                        for i = 1:m
                            R(i,j) = A(i,j);
                        end
                    end
               end

                %-------------------------------------------------------------------------- 
                ";
            var parser = new ParsingEngine();
            var statement = parser.Parse(expression, true);

            Assert.AreEqual(
                "function[Q,R,E]=qr(A,econ){eml_assert(>(nargin,0),Not enough input arguments.);eml_assert(||(==(nargin,1),&&(isa(econ,numeric),isscalar(econ))),Use qr(X,0) for economy size decomposition.);eml_assert(isa(A,float),[Function ''qr'' is not defined for values of class '',class(A),''.]);eml_lib_assert(==(ndims(A),2),EmbeddedMATLAB:qr:inputMustBe2D,Argument A must be a 2-D matrix.);eml_lib_assert(||(==(nargin,1),==(econ,0)),MATLAB:qr:unknownOptionForEconomySizeDecomposition,Use qr(X,0) for economy size decomposition.);if(==(nargout,3)){if(==(nargin,1)){[Q,R,E]=qr_full(A)}else{[Q,R,E]=qr_econ(A)}}else{if(==(nargout,2)){if(==(nargin,1)){[Q,R]=qr_full(A)}else{[Q,R]=qr_econ(A)}}else{[Q]=eml_zlaqp2(A)}};function[Q,R,E]=qr_full(A){eml_must_inline;[m]=size(A,1);[n]=size(A,2);if(~(eml_is_const(n))){.(eml,varsize)(Q,tau)}else{};[Q]=.(eml,nullcopy)(eml_expand(eml_scalar_eg(A),[m,m]));[R]=.(eml,nullcopy)(A);if(==(nargout,3)){[E]=zeros(n,class(A))}else{};if(>(m,n)){for(j=:(1,n)){for(i=:(1,m)){[Q(i,j)]=A(i,j)}};for(j=:(+(n,1),m)){for(i=:(1,m)){[Q(i,j)]=0}};if(==(nargout,3)){[Q,tau,jpvt]=eml_zlaqp2(Q)}else{[Q,tau]=eml_zlaqp2(Q)};for(j=:(1,n)){for(i=:(1,j)){[R(i,j)]=Q(i,j)};for(i=:(+(j,1),m)){[R(i,j)]=0}};[Q]=eml_zung2r(Q,tau)}else{if(==(nargout,3)){[A,tau,jpvt]=eml_zlaqp2(A)}else{[A,tau]=eml_zlaqp2(A)};for(j=:(1,m)){for(i=:(1,j)){[R(i,j)]=A(i,j)};for(i=:(+(j,1),m)){[R(i,j)]=0}};for(j=:(+(m,1),n)){for(i=:(1,m)){[R(i,j)]=A(i,j)}};[Q]=eml_zung2r(A,tau)};if(==(nargout,3)){for(k=:(1,n)){[E(jpvt(k),k)]=1}}else{};function[Q,R,E]=qr_econ(A){eml_must_inline;if(==(nargout,3)){[A,tau,jpvt]=eml_zlaqp2(A);[E]=double(jpvt)}else{[A,tau]=eml_zlaqp2(A)};[Q]=eml_zung2r(A,tau);[m]=size(A,1);[n]=size(A,2);if(>(m,n)){[R]=.(eml,nullcopy)(eml_expand(eml_scalar_eg(A),[n,n]));for(j=:(1,n)){for(i=:(1,j)){[R(i,j)]=A(i,j)};for(i=:(+(j,1),n)){[R(i,j)]=0}}}else{[R]=.(eml,nullcopy)(A);for(j=:(1,m)){for(i=:(1,j)){[R(i,j)]=A(i,j)};for(i=:(+(j,1),m)){[R(i,j)]=0}};for(j=:(+(m,1),n)){for(i=:(1,m)){[R(i,j)]=A(i,j)}}}}}}",
                statement.ToString());
        }

        [TestMethod]
        public void CellTest()
        {
            var parser = new ParsingEngine();
            var expression1 = "{1,2,3}";
            var statement1 = parser.Parse(expression1);

            Assert.AreEqual("{1,2,3}", statement1.ToString());

            var expression2 = "A{1,2,3}";
            var statement2 = parser.Parse(expression2);
            Assert.AreEqual("A{1,2,3}", statement2.ToString());
        }

        //[TestMethod]
        //public void Fix_1_Test()
        //{
        //    /* TODO: popraviti gresku u lekseru koja izraz
        //     * "[a , ... \n b]" prevodi u 
        //     * '[' NAME ',' ',' NAME ']'
        //     * umesto u
        //     * '[' NAME ',' NAME ']' 
        //     */

        //    var expression = "[a , ...\n b]";
        //    var parser = new ParsingEngine();
        //    var statement = parser.Parse(expression);
        //    Assert.AreEqual("[a,b]", statement.ToString());
        //}

    }
}
